package com.dongbao.core.rules.java;

import com.dongbao.core.base.CodeRulesCommonBase;
import com.dongbao.core.rules.CodeRulesBase;
import com.dongbao.entity.AwardedInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.impl.source.tree.java.*;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 *  for循环体内,循环调用增删改查数据库或查询编号, 提取for外层, 可采用批量查询方式进行一次性集合查询，大大提高性能.
 * */
public class ForDBOperationRules extends CodeRulesCommonBase implements CodeRulesBase {

    private static final Logger LOGGER = Logger.getInstance(ForDBOperationRules.class);

    private final List<String> allKeyName=Arrays.asList("insert","save","delete","update");

    public ForDBOperationRules(VirtualFile afterFile,PsiFile psiFile,Project project) {
        super(afterFile,psiFile,project);
        this.code = "A013";
        this.level = "A";
    }
    /**
     * 执行规则判断
     * */
    @Override
    public void run(List<AwardedInfo> deduction) {
        /* 执行分析当前class是否负责当前规则 */
        PsiAnnotation[] annotations = psiClassImpls.get(0).getModifierList().getAnnotations();
        for(PsiClassImpl psiClass:psiClassImpls){
            /* 获取所有方法 */
            List<PsiMethod> ownMethods = psiClass.getOwnMethods();
            for(PsiMethod method:ownMethods){
                /* 从方法中查到看是否存在for循环 */
                if(method.getBody()!=null){
                    PsiElement[] children = method.getBody().getChildren();
                    for(PsiElement childrenItem:children){
                        /* 判断当前节点是否是for循环节点 */
                        if(childrenItem instanceof PsiForStatementImpl){
                            forBody(childrenItem,deduction,psiClass,method,1);
                        }
                        /* 判断是否是if,如果是if标签则再进到if里面去处理内部代码判断 */
                        if(childrenItem instanceof PsiIfStatementImpl){
                            ifCode(childrenItem,deduction,psiClass,method,0);
                        }
                    }
                }
            }
        }
    }

    /**
     * 处理IF内逻辑代码 判断是否存在for
     * */
    public void ifCode(PsiElement element,List<AwardedInfo> deduction,PsiClassImpl psiClass,PsiMethod method,Integer isFor){
        /* 从IF中取出代码块 */
        PsiElement psiElement = Arrays.stream(element.getChildren()).filter(item -> item instanceof PsiBlockStatementImpl).findAny().orElse(null);
        if(psiElement==null){
            return ;
        }
        PsiElement ifBody = psiElement.getLastChild();
        PsiElement[] children = ifBody.getChildren();
        /* 使用循环过滤掉无限元素与if或者for循环 */
        for (PsiElement child : children) {
            validExp(child,deduction,psiClass,method,isFor);
        }
    }

    /**
     * 处理for循环节点中的代码,判断是否存在数据库操作
     * */
    public void forBody(PsiElement childrenItem,List<AwardedInfo> deduction,PsiClassImpl psiClass,PsiMethod method,Integer isFor){
        /* 获取for循环中的所有子节点 */
        if(childrenItem.getChildren().length==0){
            return ;
        }
        PsiElement psiElement = Arrays.stream(childrenItem.getChildren()).filter(item -> item instanceof PsiBlockStatementImpl).findAny().orElse(null);
        if(psiElement==null){
            return ;
        }
        PsiElement ifBody = psiElement.getLastChild();
        PsiElement[] forChildreElements = ifBody.getChildren();
        /* 去掉所有子节点中的注释 */
        List<PsiElement> filterChildreElements = Arrays.stream(forChildreElements).filter(item -> {
            return !(item instanceof PsiComment);
        }).collect(Collectors.toList());
        /* 遍历所有节点 判断是否存在if for 或者当前方法是操作数据库的语句 */
        for(PsiElement elementRow:filterChildreElements){
            validExp(elementRow,deduction,psiClass,method,isFor);
        }
    }

    /**
     * 判断表达式是否存在操作数据库的关键字
     * */
    public void validExp(PsiElement elementRow,List<AwardedInfo> deduction,PsiClassImpl psiClass,PsiMethod method,Integer isFor){

        /* 判断当前节点是否是for循环节点 */
        if(elementRow instanceof PsiForStatementImpl){
            forBody(elementRow,deduction,psiClass,method,1);
        }
        /* 判断是否是if,如果是if标签则再进到if里面去处理内部代码判断 */
        if(elementRow instanceof PsiIfStatementImpl){
            ifCode(elementRow,deduction,psiClass,method,isFor);
        }

        /* 判断如果是表达式就直接判断是否是操作数据库的关键字 */
        if(elementRow instanceof PsiExpressionStatement && isFor == 1){
            /* 判断是否存在操作数据库的关键字 */
            List<String> dbExpression = allKeyName.stream().filter(item -> {
                return elementRow.getText().contains(item);
            }).collect(Collectors.toList());
            if(!dbExpression.isEmpty()){
                /* 获取错误代码行位置 */
                Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                int lineNumber = document.getLineNumber(((PsiExpressionStatementImpl) elementRow).getStartOffset());

                packDeduction(
                        deduction,
                        psiClass.getName(),
                        psiPackageStatements.get(0).getPackageName(),
                        method.getName(),
                        "for循环体内,循环调用增删改查数据库或查询编号, 提取for外层, 可采用批量查询方式进行一次性集合查询，大大提高性能.",lineNumber,lineNumber);
            }
        }
    }

    @Override
    public String describe() {
        return "性能优化";
    }
}
